﻿''' <summary>
''' Muggers Directly Stolen from the ObjectListView; Still need to Steel A Few
''' </summary>
''' <remarks></remarks>
''' <stepthrough>Enabled</stepthrough>
<DebuggerStepThrough()> <Obsolete("Use the one In Campf", True)>
Public NotInheritable Class Muggers
    Private Sub New()
    End Sub


    ''' <summary>
    ''' A SimpleMunger deals with a single property/field/method on its target.
    ''' </summary>
    ''' <remarks>
    ''' Munger uses a chain of these resolve a dotted aspect name.
    ''' </remarks>
    Public Class SimpleMunger
        Private m_aspectName As String
        '<DebuggerStepThrough()>

#Region "       Implementation     >>>"

        Private Sub ResolveName(ByVal target As Object, ByVal name As String, ByVal numberMethodParameters As Integer)

            If cachedTargetType = target.[GetType]() AndAlso cachedName = name AndAlso cachedNumberParameters = numberMethodParameters Then
                Return
            End If

            cachedTargetType = target.[GetType]()
            cachedName = name
            cachedNumberParameters = numberMethodParameters

            resolvedFieldInfo = Nothing
            resolvedPropertyInfo = Nothing
            resolvedMethodInfo = Nothing
            indexerPropertyInfo = Nothing

            '| BindingFlags.NonPublic
            Const flags As Reflection.BindingFlags = Reflection.BindingFlags.[Public] Or Reflection.BindingFlags.Instance

            For Each pinfo As Reflection.PropertyInfo In target.[GetType]().GetProperties(flags)
                If pinfo.Name = name Then
                    resolvedPropertyInfo = pinfo
                    Return
                End If

                ' See if we can find an string indexer property while we are here.
                ' We also need to allow for old style <object> keyed collections.
                If indexerPropertyInfo Is Nothing AndAlso pinfo.Name = "Item" Then
                    Dim parameterType As Type = pinfo.GetGetMethod().GetParameters()(0).ParameterType
                    If parameterType = GetType(String) OrElse parameterType = GetType(Object) Then
                        indexerPropertyInfo = pinfo
                    End If
                End If
            Next

            For Each info As Reflection.FieldInfo In target.[GetType]().GetFields(flags)
                If info.Name = name Then
                    resolvedFieldInfo = info
                    Return
                End If
            Next

            For Each info As Reflection.MethodInfo In target.[GetType]().GetMethods(flags)
                If info.Name = name AndAlso info.GetParameters().Length = numberMethodParameters Then
                    resolvedMethodInfo = info
                    Return
                End If
            Next
        End Sub

        Private cachedTargetType As Type
        Private cachedName As String
        Private cachedNumberParameters As Integer

        Private resolvedFieldInfo As Reflection.FieldInfo
        Private resolvedPropertyInfo As Reflection.PropertyInfo
        Private resolvedMethodInfo As Reflection.MethodInfo
        Private indexerPropertyInfo As Reflection.PropertyInfo

#End Region


        ''' <summary>
        ''' The name of the aspect that is to be peeked or poked.
        ''' </summary>
        ''' <remarks>
        ''' <para>
        ''' This name can be a field, property or method. 
        ''' When using a method to get a value, the method must be parameter-less.
        ''' When using a method to set a value, the method must accept 1 parameter.
        ''' </para>
        ''' <para>
        ''' It cannot be a dotted name.
        ''' </para>
        ''' </remarks>
        Public ReadOnly Property AspectName() As String
            Get
                Return m_aspectName
            End Get
        End Property

        ''' <summary>
        ''' Create a SimpleMunger
        ''' </summary>
        ''' <param name="aspectName"></param>
        Public Sub New(ByVal aspectName As [String])
            Me.m_aspectName = aspectName
        End Sub


        ''' <summary>
        ''' Get a value from the given target
        ''' </summary>
        ''' <param name="target"></param>
        ''' <returns></returns>
        Public Function GetValue(ByVal target As [Object]) As [Object]
            If target Is Nothing Then
                Return Nothing
            End If

            Me.ResolveName(target, Me.AspectName, 0)

            Try
                If Me.resolvedPropertyInfo IsNot Nothing Then
                    Return Me.resolvedPropertyInfo.GetValue(target, Nothing)
                End If

                If Me.resolvedMethodInfo IsNot Nothing Then
                    Return Me.resolvedMethodInfo.Invoke(target, Nothing)
                End If

                If Me.resolvedFieldInfo IsNot Nothing Then
                    Return Me.resolvedFieldInfo.GetValue(target)
                End If

                ' If that didn't work, try to use the indexer property. 
                ' This covers things like dictionaries and DataRows.
                If Me.indexerPropertyInfo IsNot Nothing Then
                    Return Me.indexerPropertyInfo.GetValue(target, New Object() {Me.AspectName})
                End If
            Catch ex As Exception
                ' Lots of things can do wrong in these invocations
                Throw New MungerException(Me, target, ex)
            End Try

            ' If we get to here, we couldn't find a match for the aspect
            Throw New MungerException(Me, target, New MissingMethodException())
        End Function

        ''' <summary>
        ''' Poke the given value into the given target indicated by our AspectName.
        ''' </summary>
        ''' <param name="target">The object that will be poked</param>
        ''' <param name="value">The value that will be poked into the target</param>
        ''' <returns>bool indicating if the put worked</returns>
        Public Function PutValue(ByVal target As Object, ByVal value As Object) As Boolean
            If target Is Nothing Then
                Return False
            End If

            Me.ResolveName(target, Me.AspectName, 1)

            Try
                If Me.resolvedPropertyInfo IsNot Nothing Then
                    Me.resolvedPropertyInfo.SetValue(target, value, Nothing)
                    Return True
                End If

                If Me.resolvedMethodInfo IsNot Nothing Then
                    Me.resolvedMethodInfo.Invoke(target, New Object() {value})
                    Return True
                End If

                If Me.resolvedFieldInfo IsNot Nothing Then
                    Me.resolvedFieldInfo.SetValue(target, value)
                    Return True
                End If

                ' If that didn't work, try to use the indexer property. 
                ' This covers things like dictionaries and DataRows.
                If Me.indexerPropertyInfo IsNot Nothing Then
                    Me.indexerPropertyInfo.SetValue(target, value, New Object() {Me.AspectName})
                    Return True
                End If
            Catch ex As Exception
                ' Lots of things can do wrong in these invocations
                Throw New MungerException(Me, target, ex)
            End Try

            Return False
        End Function

    End Class



    ''' <summary>
    ''' These exceptions are raised when a munger finds something it cannot process
    ''' </summary>
    <DebuggerStepThrough()>
    Public Class MungerException
        Inherits ApplicationException
        ''' <summary>
        ''' Create a MungerException
        ''' </summary>
        ''' <param name="munger"></param>
        ''' <param name="target"></param>
        ''' <param name="ex"></param>
        Public Sub New(ByVal munger As SimpleMunger, ByVal target As Object, ByVal ex As Exception)
            MyBase.New("Munger failed", ex)
            Me.m_munger = munger
            Me.m_target = target
        End Sub

        ''' <summary>
        ''' Get the munger that raised the exception
        ''' </summary>
        Public ReadOnly Property Munger() As SimpleMunger
            Get
                Return m_munger
            End Get
        End Property
        Private m_munger As SimpleMunger

        ''' <summary>
        ''' Gets the target that threw the exception
        ''' </summary>
        Public ReadOnly Property Target() As Object
            Get
                Return m_target
            End Get
        End Property
        Private m_target As Object
    End Class


End Class
